package net.midnighttd.engine.entity.Pathfinder;

import net.midnighttd.engine.Engine;

import java.util.ArrayList;
import java.util.PriorityQueue;

/**
 * Author: Adam Scarr
 * Date: 24/04/2009
 * Time: 5:53:40 PM
 */
public class PathfinderTask implements Runnable {
    private int startX, startY, endX, endY;
    private boolean flying;
    private PathFoundListener pathFoundListener;
    private Engine engine;

    public PathfinderTask(Engine engine, int startX, int startY, int endX, int endY, boolean flying, PathFoundListener pathFoundListener) {
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;
        this.flying = flying;
        this.engine = engine;
        this.pathFoundListener = pathFoundListener;
    }

    public PathfinderTask(Engine engine, int startX, int startY, int endX, int endY, boolean flying) {
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;
        this.flying = flying;
        this.engine = engine;
    }

    public void update(PathfinderTask pathfinderTask) {
        this.startX = pathfinderTask.startX;
        this.startY = pathfinderTask.startY;
        this.endX = pathfinderTask.endX;
        this.endY = pathfinderTask.endY;
        this.flying = pathfinderTask.flying;
        this.engine = pathfinderTask.engine;
    }

    public boolean equals(Object o) {
        if (o instanceof PathfinderTask) {
            return this.pathFoundListener == ((PathfinderTask)o).pathFoundListener;
        }
        return false;
    }

    public int getStartX() {
        return startX;
    }

    public int getStartY() {
        return startY;
    }

    public int getEndX() {
        return endX;
    }

    public int getEndY() {
        return endY;
    }

    public boolean isFlying() {
        return flying;
    }

     public Path findPath() {
        ArrayList<MapNode> closedset = new ArrayList<MapNode>();
        PriorityQueue<MapNode> openset = new PriorityQueue<MapNode>();

        openset.add(new MapNode(startX, startY, endX, endY, null));

        MapNode node;
        while((node = openset.poll()) != null) {
            if(node.getX() == endX && node.getY() == endY) return node.getPath();
            closedset.add(node);

            for(MapNode neighbor: node.getNeighbors()) {
                if(closedset.contains(neighbor)) continue;
                if(!canPass(neighbor.getX(), neighbor.getY(), flying)) continue;

                if(!openset.contains(neighbor)) {
                    openset.add(neighbor);

                } else {
                    for(MapNode existing: openset) {
                        if(existing.equals(neighbor)) {
                            if(existing.G() > neighbor.G()) {
                                openset.remove(existing);
                                openset.add(neighbor);
                            }
                            break;
                        }
                    }
                }
            }
        }

        return null;
    }

    public boolean canPass(int x, int y, boolean flying) {
        return engine.getMapManager().isPassable(x, y) &&
               (flying || engine.getEntityManager().isPassable(x,y));
    }

    public void run() {
        pathFoundListener.pathFound(findPath());
    }
}
